package jp.tkym.labs.store;

import java.util.ArrayList;
import java.util.List;

import jp.tkym.labs.util.prop.PropertyResolver;
import jp.tkym.labs.util.prop.PropertyResolverProvider;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;

import jp.tkym.labs.store.EntityResolveUtil.KeyResolveType;

/**
 * EntityResolver
 * 
 * @author tkym
 * @param <B>
 */
class EntityResolver<B>{
	
	/**
	 * Key Resolve By ID
	 */
	private KeyResolveType keyResolveType = KeyResolveType.ID;
	
	private PropertyResolver<B> resolver;
	
	/**
	 * Parent's Key
	 */
	private Key parent;
	
	private List<String> indexedProperties;
	
	/**
	 * Key kind is 
	 * use simple class name 
	 * 
	 */
	private String kind;
	private String keyProperty;
	private EntityResolveUtil util = EntityResolveUtil.getInstance();
	
	EntityResolver(Class<B> cls, Key parent, String keyProperty){
		resolver = PropertyResolverProvider.getInstance().get(cls);
		this.kind = cls.getSimpleName();
		this.parent = parent;
		this.keyProperty = keyProperty;
		this.indexedProperties = new ArrayList<String>();
		checkMemverValues();
	}
	
	/**
	 * check member values
	 */
	void checkMemverValues(){
		if(keyProperty == null)
			throw new IllegalArgumentException("keyProperty is null");
			
		Class<?> type = resolver.getTypeOf(keyProperty);
		if(type == null)
			throw new IllegalArgumentException("keyProperty is illegal [" +keyProperty+ "]");
		
		keyResolveType = util.resolveKeyType(type);
		if(keyResolveType == KeyResolveType.NONE)
			throw new IllegalArgumentException(
					"keyProperty type is illegal [" 
					+keyProperty+ ", type="+type.getName()+"]");
	}
	
	void addIndexedProperty(String propertyName){
		this.indexedProperties.add(propertyName);
	}
	
	/**
	 * create Key by id
	 * @param id 
	 * @return Key
	 */
	public Key toKey(long id){
		if(keyResolveType == KeyResolveType.NAME) 
			throw new IllegalArgumentException("");
		return KeyFactory.createKey(parent, kind, id);
	}
	
	/**
	 * create Key by name
	 * @param name key name
	 * @return Key
	 */
	public Key toKey(String name){
		return KeyFactory.createKey(parent, kind, name);
	}
	
	/**
	 * toKey
	 * @param bean
	 * @return
	 */
	Key toKey(B bean){
		if(keyResolveType == KeyResolveType.ID)
			return toKey((Long)resolver.get(bean, keyProperty));
		if(keyResolveType == KeyResolveType.NAME)
			return toKey((String)resolver.get(bean, keyProperty));
		throw new IllegalArgumentException();
	}
	
	/**
	 * create Entity from Bean
	 * @param bean :<B> 
	 * @return Entity
	 */
	public Entity toEntity(B bean){
		Entity entity = new Entity(toKey(bean));
		for(String propertyName : resolver.propertyNames()){
			if(this.indexedProperties.contains(propertyName)){
				entity.setProperty(propertyName, resolver.get(bean, propertyName));
			}else{
				entity.setUnindexedProperty(propertyName, resolver.get(bean, propertyName));
			}
		}
		return entity;
	}
	
	/**
	 * create Bean from Entity
	 * @param entity
	 * @return
	 */
	public B toBean(Entity entity){
		B bean = resolver.createInstance();
		for(String propertyName : resolver.propertyNames()){
			Object entityValue = entity.getProperty(propertyName);
			Class<?> typeOfBean = resolver.getTypeOf(propertyName);
			Object beanValue = null;
			try{
				beanValue = util.convertFrom(typeOfBean, entityValue);
			}catch(RuntimeException e){
				System.out.println(
						"property name is " + propertyName + 
						" expected type is " + typeOfBean.getName() + 
						" entityValue is " + entityValue);
				throw e;
			}
			resolver.set(bean, propertyName, beanValue); 
		}
		return toBeanFromKey(bean, entity.getKey());
	}
	
	/**
	 * 
	 * @param bean
	 * @param key
	 * @return
	 */
	B toBeanFromKey(B bean, Key key){
		if(keyResolveType == KeyResolveType.ID)
			resolver.set(bean, keyProperty, key.getId());
		if(keyResolveType == KeyResolveType.NAME)
			resolver.set(bean, keyProperty, key.getName());
		return bean;
	}
		
	/**
	 * create Query instance
	 * @return
	 */
	Query createQuery(){
		return new Query(kind, parent);
	}
}						